
//
// 101020 - AH - Created but just as a place holder nop class.
// 101116 - AH - Now holds spokes.
// 101122 - AH - Allow all spokes to be null and added no arg. constructor.
// 101123 - AH - Implements now SoekratData and Wheel(SoekratData).
// 101206 - AH - Made getForest() and getNetwork() behave like getMine() :
//               create empty Forest / Network and spoke if does not exists.
// 101214a- AH - (Hector) Now invokes all spokes' scanAndPaint methods in this'
//               scanAndPaint method.
// 101214b- AH - (Hector) For now do only invoke scanAndPaint method for 
//               ForestSpoke for testing purposes.
// 101219 - AH - Now invoke all spoke's scanAndPaint methods, system handles it
//               now.
// 110108 - AH - Now invoke new rinse() method on Spoke objects before 
//               retrieval. 
// 110109a- AH - Did java doc todo of method scanAndPaint(Set<String>). 
// 110109b- AH - forceMine(), forceForest(), forceNetwork() and unforce().
// 110109c- AH - unforce() renamed to free().
// 110109d- AH - getRetrievalEventTree().
// 110109 - AH - (Hector) Introducing RetrievalEventTree.
// 110110a- AH - getRandomSpoke().
// 110110b- AH - getNextSpoke().
// 110111 - AH - (Fantoft) Removed code here that is now Motor responsibility.
// 110112a- AH - (Fantoft) Renamed getRandomSpoe to getFirstSpoke.
// 110112b- AH - (Fantoft) setForced(Spoke) is back and setFirst(Spoke).
// 110112c- AH - (Fantoft) getSpokes().
// 110112d- AH - (Fantoft) freeSpokes().
// 110117 - AH - (Hector) Now getNextSpoke(Spoke) takes into account that 
//               all spokes are functional in the Wheel.
// 110119a- AH - (Hector) Removed nextSpoke() and added getSpokesForTurn().
// 110119b- AH - (Hector) getNumberOfSpokes().
// 110121a- AH - (Bien) getRandomSpoke().
// 110121b- AH - (Bien) getShuffledSpokes().
// 110122 - AH - Default constructor now creates empty onotologies and their
//               spokes.
// 110123a- AH - getSpoke(Class<? extends Spoke>).
// 110123b- AH - (Bien) setNumberOfSpokesToUse(int) and 
//               getNumberOfSpokesToUse().
// 110123c- AH - (Bien) Made fire SoekratEvent objects.
// 110124 - AH - Extends SoekratEngineObject.
// 110130a- AH - getSpokeControl().
// 110130b- AH - (Bien) Took SpokeControl in use for passing Spoke objects to
//               the Motor.
// 110203 - AH - (Fantoft) Improved layout of code for readability.
// 110325 - AH - getSpareWheelNetwork() returns null here.
// 110423 - AH - Makes this has Pit, removed not anymore used constructor that
//               accepts the three ontologies Forest, Mine and Network.
// 110530a- AH - (Chaos) getTitle() and setTitle().
// 110530b- AH - (Chaos) sets the title from passed SoekratData in constructor
//               Wheel(SoekratData).
// 110502 - AH - getSpoke(idx).
// 110716 - AH - (Bien *) getMotorConfiguration() that returns null.
//

package org.soekrat.engine.wheel;

import static org.soekrat.engine.util.Util.getRandom;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.soekrat.engine.SoekratEngineObject;
import org.soekrat.engine.forest.Forest;
import org.soekrat.engine.io.SoekratData;
import org.soekrat.engine.mine.Mine;
import org.soekrat.engine.motor.passingstrategies.SequenceWheelProcessing;
//import org.soekrat.engine.motor.MotorConfiguration;
import org.soekrat.engine.network.Network;
import org.soekrat.engine.ontology.OntologyDatastructure;
import org.soekrat.engine.ontology.ptable.ProductTable;
import org.soekrat.engine.pit.Pit;
import org.soekrat.engine.swnetwork.SpareWheelNetwork;
import org.soekrat.engine.wheel.ontology.ForestOntology;
import org.soekrat.engine.wheel.ontology.MineOntology;
import org.soekrat.engine.wheel.ontology.NetworkOntology;
import org.soekrat.engine.wheel.ontology.Ontology;
import org.soekrat.engine.wheel.ontology.ProductTableOntology;

/**
 * <p>
 *   The wheel is responsible for finding items in the 
 *   {@link Forest}, 
 *   {@link Network} or the 
 *   {@link Mine} given keywords found from end user input.
 * </p>
 * @author Laetitia Le Chatton (LLC) 
 * @author Arne Halvorsen (AH)
 */
public final class Wheel extends SoekratEngineObject implements SoekratData 
{
  private String m_title=null;
  
  private Pit m_pit=null;
  
  private final List<Ontology> m_ontologies=new ArrayList<Ontology>();
  
  private final List<Class<?>> m_pattern=new ArrayList<Class<?>>();
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public Wheel()
  { 
    init(new Pit(), new Mine(), new Forest(), new Network(), 
      new ProductTable()); 
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param sd {@link SoekratData}.
   */
  public Wheel(SoekratData sd)
  {
    if (sd==null)
    {
      throw new NullPointerException("sd");
    }
    
    m_title=sd.getTitle();
    
    init(sd.getPit(), sd.getMine(), sd.getForest(), sd.getNetwork(), 
      sd.getProductTable());
  }
  
  /**
   * <p>
   *   Sets the sequence of ontologies.
   * </p>
   * @param oclasses Classes.
   */
  @Deprecated
  public void setSpokeSequence(List<Class<?>> oclasses)
  {
    if (oclasses==null)
    {
      throw new NullPointerException("oclasses");
    }
    
    m_pattern.clear();
    m_pattern.addAll(oclasses);
  }
  
  /**
   * <p>
   *   Gets number of 
   *   {@link Ontology}s {@code this}
   *   {@code Wheel} has.
   * </p>
   * @return Count.
   */
  public int getNumberOfOntologies(){ return m_ontologies.size(); }
  
  /**
   * <p>
   *   Gets ontology based on qualified class name.
   * </p>   
   * @param cname Class name.
   * @return Ontololgy or {@code null} if not found.
   */
  public Ontology getOntology(String cname)
  {
    if (cname==null)
    {
      throw new NullPointerException("cname");
    }
    
    for (Ontology curr : m_ontologies)
    {
      if (curr.getClass().getName().equals(cname)) return curr;  
    }
    return null;
  }
  
  /**
   * <p>
   *   Gets 
   *   {@link Ontology}.
   * </p>
   * @param idx Index of 
   *            {@link Ontology} to get.
   * @return {@link Ontology}.
   * @throws IndexOutOfBoundsException If 
   *         {@code idx<0 || idx>=getNumberOfSpokes()}.
   */
  public Ontology getOntology(int idx){ return m_ontologies.get(idx); }
  
  // Invoked from constructors.
  @Deprecated
  private void init(Pit pit, Mine mine, Forest forest, Network network, 
    ProductTable pt)
  {
    m_pit=(pit==null) ? new Pit() : pit;
    
    mine=(mine==null) ? new Mine() : mine;
    forest=(forest==null) ? new Forest() : forest;
    network=(network==null) ? new Network() : network;
    pt=(pt==null) ? new ProductTable() : pt;
    
    m_ontologies.add(new MineOntology(this, mine));
    m_ontologies.add(new ForestOntology(this, forest));
    m_ontologies.add(new NetworkOntology(this, network));
    m_ontologies.add(new ProductTableOntology(this, pt));
    
    m_pattern.add(Forest.class);
  }
  
  /**
   * <p>
   *   Gets the
   *   {@link Ontology}s.
   * </p>
   * @return {@link Ontology}s.
   */
  public Ontology[] getOntologies()
  { 
    return m_ontologies.toArray(Ontology.EMPTY_ARRAY); 
  }

  /**
   * <p>
   *   Gets random 
   *   {@link Ontology}.
   * </p>
   * @return Random 
   *         {@link Ontology}.
   */
  public Ontology getRandomOntology(){ return getRandom(m_ontologies); }
  
  /**
   * <p>
   *   Gets the
   *   {@link Ontology}s of {@code this} in a random order.
   * </p>
   * @return {@link Ontology}s.
   */
  public List<Ontology> getShuffledOntologies()
  {
    List<Ontology> spokes=new ArrayList<Ontology>(m_ontologies);
    Collections.shuffle(spokes);
    return spokes;
  }
  
  /**
   * <p>
   *   Gets number of 
   *   {@link Ontology}s that should be used.
   * </p>
   * @return Count or {@code 0} if not said.
   */
  @Deprecated
  public int getNumberOfSpokesToUse(){ return m_pattern.size(); }
  
  /**
   * <p>
   *   Gets 
   *   {@link Ontology} given it's ontology's class.
   * </p>
   * @param clazz Ontology.
   * @return {@link Ontology}.
   */
  public Ontology getOntologyGivenOntologyClass(Class<?> clazz)
  {
    for (Ontology curr : m_ontologies)
    {
      Class<?> c=curr.getOntologyDatastructure().getClass();
      if (clazz==c)
      {
        return curr;
      }
    }
    return null;
  }
  
  /**
   * <p>
   *   Gets the 
   *   {@link Ontology}s that will be used to in the next turn.
   * </p>
   * <p>
   *   The 
   *   {@code Spoke} are to be used in the sequence given.
   * </p>
   * @return {@link Ontology}.
   */
  @Deprecated
  public List<Ontology> getOntologiesForTurn()
  {
    List<Ontology> retVal=new ArrayList<Ontology>();
    for (Class<?> curr : m_pattern)
    {
      Ontology spoke=getOntologyGivenOntologyClass(curr);
      if (spoke!=null) retVal.add(spoke);
    }
    
    return retVal;
  }
  
  /**
   * <p>
   *   Sets title that should be used for data set.
   * </p>
   * @param title Title, if {@code null} or the empty 
   *              {@code String}
   *              {@link #getTitle()} will return 
   *              {@link #getName()} else returns {@code title}.
   */
  public void setTitle(String title)
  {
    m_title=(title==null) ? "" : title.trim();
    m_title=(title.length()==0) ? null : title; 
  }

  // Specified in SoekratData.
  public String getName(){ return "soekrat"; }
  
  // Specified in SoekratData.
  public String getTitle()
  {
    return (m_title==null) ? getName() : m_title;
  }
  
  @Override
  public OntologyDatastructure getOntologyDatastructure(String name) 
  {
    if (name==null)
    {
      throw new NullPointerException("name");
    }
    
    for (Ontology curr : m_ontologies)
    {
      OntologyDatastructure o=curr.getOntologyDatastructure();
      if (o.getClass().getName().equals(name)) return o;   
    }
    
    return null;
  }

  /**
   * <p>
   *   Gets
   *   {@link OntologyDatastructure} managed by a
   *   {@link Ontology} of {@code this}
   *   {@code Wheel}.
   * </p>
   * @param clazz {@code Class} of 
   *              {@link OntologyDatastructure} to get. 
   * @return {@link OntologyDatastructure} or {@code null} if not found.
   * @throws IllegalArgumentException If not found.
   */
  public OntologyDatastructure getOntologyDatastructure(Class<?> clazz)
  {
    if (clazz==null)
    {
      throw new NullPointerException("clazz");
    }
    
    for (Ontology curr : m_ontologies)
    {
      OntologyDatastructure o=curr.getOntologyDatastructure();
      if (o.getClass()==clazz) return o;   
    }
    
    throw new IllegalArgumentException("Ontology of clazz: '"+clazz.getName()+
      "' not found");
  } 
  
  // Specified in SoekratData.
  public SequenceWheelProcessing getSequenceWheelProcessing(){ return null; }

  // Specified in SoekratData.
  public Mine getMine()
  { 
    return (Mine)getOntologyDatastructure(Mine.class);
  }
  
  // Specified in SoekratData.
  public Forest getForest()
  { 
    return (Forest)getOntologyDatastructure(Forest.class); 
  }
  
  // Specified in SoekratData.
  public Network getNetwork()
  { 
    return (Network)getOntologyDatastructure(Network.class);
  }
  
  // Specified in SoekratData.
  public ProductTable getProductTable()
  { 
    return (ProductTable)getOntologyDatastructure(ProductTable.class);
  }
  
  // Specified in SoekratData.
  public SpareWheelNetwork getSpareWheelNetwork(){ return null; }
  
  // Specified in SoekratData.
  public Pit getPit(){ return m_pit; }
  
}
